#pragma once
#include <memory>
#include <atomic>
#include <string>
#include <vector>
#include <mutex>
#include <cstdarg>
#include "formatitem.hpp"
#include "sink.hpp"
#include "looper.hpp"
namespace mylog
{
    class Logger
    {
    public:
        enum Type
        {
            LOGGER_SYNC = 0,
            LOGGER_ASYNC
        };

        using ptr = std::shared_ptr<Logger>;

    public:
        Logger(const std::string &name, Formater::ptr formatter, std::vector<LogSink::ptr> &sinks, LogLevel::Level level = LogLevel::Level::DEBUG) : _name(name),
                                                                                                                                                     _level(level),
                                                                                                                                                     _formatter(formatter),
                                                                                                                                                     _sinks(sinks.begin(), sinks.end())
        {
        }
        std::string LoggerName() { return _name; }
        LogLevel::Level LoggerLevel() { return _level; }
        void Debug(const char *file, size_t line, const char *fmt, ...)
        {
            if (shouldLog(LogLevel::Level::DEBUG) == false)
            {
                return;
            }
            va_list al;
            va_start(al, fmt);
            log(LogLevel::Level::DEBUG, file, line, fmt, al);
            va_end(al);
        }
        void Info(const char *file, size_t line, const char *fmt, ...)
        {
            if (shouldLog(LogLevel::Level::INFO) == false)
            {
                return;
            }
            va_list al;
            va_start(al, fmt);
            log(LogLevel::Level::INFO, file, line, fmt, al);
            va_end(al);
        }
        void Warn(const char *file, size_t line, const char *fmt, ...)
        {
            if (shouldLog(LogLevel::Level::WARN) == false)
            {
                return;
            }
            va_list al;
            va_start(al, fmt);
            log(LogLevel::Level::WARN, file, line, fmt, al);
            va_end(al);
        }
        void Error(const char *file, size_t line, const char *fmt, ...)
        {
            if (shouldLog(LogLevel::Level::WARN) == false)
            {
                return;
            }
            va_list al;
            va_start(al, fmt);
            log(LogLevel::Level::ERROR, file, line, fmt, al);
            va_end(al);
        }
        void Fatal(const char *file, size_t line, const char *fmt, ...)
        {
            if (shouldLog(LogLevel::Level::FATAL) == false)
            {
                return;
            }
            va_list al;
            va_start(al, fmt);
            log(LogLevel::Level::FATAL, file, line, fmt, al);
            va_end(al);
        }

    protected:
        bool shouldLog(LogLevel::Level level) { return level >= _level; }
        void log(LogLevel::Level level, const string &filename, size_t line, const char *fmt, va_list al)
        {
            char *buf;
            string msg;
            int len = vasprintf(&buf, fmt, al);
            if (len < 0)
            {
                msg = "格式化日志消息失败！！";
            }
            else
            {
                msg.assign(buf, len);
                free(buf);
            }
            LogMsg lm(line, _name, filename, level, std::move(msg));
            std::stringstream ss;
            _formatter->format(ss, lm);
            logIt(std::move(ss.str()));
        }
        virtual void logIt(const std::string &msg) = 0;

    public:
        class Builder
        {
        public:
            using ptr = std::shared_ptr<Builder>;
            Builder() : _level(LogLevel::Level::DEBUG),
                        _logger_type(Logger::Type::LOGGER_SYNC) {}
            void BuildLoggerName(const std::string &name) { _logger_name = name; }
            void BuildLoggerLevel(LogLevel::Level level) { _level = level; }
            void BuildLoggerType(Logger::Type type) { _logger_type = type; }
            void BuildFormatter(const std::string pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n") { _formatter = std::make_shared<Formater>(pattern); }
            void BuildFormatter(const Formater::ptr &formatter) { _formatter = formatter; }

            template <typename SinkType, typename... Args>
            void BuildSink(Args &&...args)
            {
                auto sink = SinkFactory::Create<SinkType>(std::forward<Args>(args)...);
                _sinks.push_back(sink);
            }
            virtual Logger::ptr Build() = 0;

        protected:
            Logger::Type _logger_type;
            std::string _logger_name;
            LogLevel::Level _level;
            Formater::ptr _formatter;
            std::vector<LogSink::ptr> _sinks;
        };

    protected:
        std::mutex _mutex;
        std::string _name;
        Formater::ptr _formatter;
        std::atomic<LogLevel::Level> _level;
        std::vector<LogSink::ptr> _sinks;
    };

    class SyncLogger : public Logger
    {
    public:
        using ptr = std::shared_ptr<SyncLogger>;
        SyncLogger(const std::string &name,
                   Formater::ptr formatter,
                   std::vector<LogSink::ptr> &sinks,
                   LogLevel::Level level = LogLevel::Level::DEBUG) : Logger(name, formatter, sinks, level)
        {
            cout << LogLevel::ToString(level) << " 同步日志器：" << name << "创建成功...\n";
        }

    private:
        virtual void logIt(const std::string &msg)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
            {
                return;
            }
            for (auto &it : _sinks)
            {
                it->Log(msg.c_str(), msg.size());
            }
        }
    };
    class AsyncLogger : public Logger
    {
    public:
        using ptr = std::shared_ptr<AsyncLogger>;
        AsyncLogger(const std::string &name,
                    Formater::ptr formatter,
                    std::vector<LogSink::ptr> &sinks,
                    LogLevel::Level level = LogLevel::Level::DEBUG) : Logger(name, formatter, sinks, level),
                                                                      _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::backendLogIt, this, std::placeholders::_1)))
        {
            std::cout << LogLevel::ToString(level) << "异步日志器： " << name << "创建成功！... \n";
        }

    protected:
        virtual void logIt(const std::string &msg)
        {
            _looper->Push(msg);
        }
        void backendLogIt(Buffer &msg)
        {
            if (_sinks.empty())
            {
                return;
            }
            for (auto &it : _sinks)
            {
                it->Log(msg.Begin(), msg.ReadAbleSize());
            }
        }

    protected:
        AsyncLooper::ptr _looper;
    };
    class LocalLoggerBuilder : public Logger::Builder
    {
    public:
        virtual Logger::ptr Build()
        {
            if (_logger_name.empty())
            {
                std::cout << "日志器名称不能为空！！";
                abort();
            }
            if (_formatter.get() == nullptr)
            {
                std::cout << "当前日志器：" << _logger_name << " 未检测到日志格式，默认设置为[ %d{%H:%M:%S}%T%t%T[%p]%T[%c]%T%f:%l%T%m%n ]!\n";
                _formatter = std::make_shared<Formater>();
            }
            if (_sinks.empty())
            {
                std::cout << "当前日志器：" << _logger_name << " 未检测到落地方向，默认设置为标准输出！\n";
                _sinks.push_back(std::make_shared<StdoutSink>());
            }
            Logger::ptr lp;
            if (_logger_type == Logger::Type::LOGGER_ASYNC)
            {
                lp = std::make_shared<AsyncLogger>(_logger_name, _formatter, _sinks, _level);
            }
            else
            {
                lp = std::make_shared<SyncLogger>(_logger_name, _formatter, _sinks, _level);
            }
            return lp;
        }
    };

    class LoggerManager
    {
    private:
        std::mutex _mutex;
        Logger::ptr _root_logger;
        std::unordered_map<string, Logger::ptr> _loggers;

    private:
        LoggerManager()
        {
            std::unique_ptr<LocalLoggerBuilder> slb(new LocalLoggerBuilder());
            slb->BuildLoggerName("root");
            slb->BuildLoggerType(Logger::Type::LOGGER_SYNC);
            _root_logger = slb->Build();
            _loggers.insert(std::make_pair("root", _root_logger));
        }
        LoggerManager(const LoggerManager &) = delete;
        LoggerManager &operator=(const LoggerManager &) = delete;

    public:
        static LoggerManager &GetInstance()
        {
            static LoggerManager lm;
            return lm;
        }
        bool HasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return false;
            }
            return true;
        }
        void AddLogger(const std::string &name, const Logger::ptr logger)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(name, logger));
        }
        Logger::ptr GetLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return Logger::ptr();
            }
            return it->second;
        }
        Logger::ptr GetRootLogger()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _root_logger;
        }
    };
    class GlobalLoggerBuilder : public Logger::Builder
    {
    public:
        virtual Logger::ptr Build()
        {
            if (_logger_name.empty())
            {
                std::cout << "日志器名称不能为空！！";
                abort();
            }
            assert(LoggerManager::GetInstance().HasLogger(_logger_name) == false);
            if (_formatter.get() == nullptr)
            {
                std::cout << "当前日志器：" << _logger_name << " 未检测到日志格式，默认设置为[ %d{%H:%M:%S}%T%t%T[%p]%T[%c]%T%f:%l%T%m%n ]!\n";
                _formatter = std::make_shared<Formater>();
            }
            if (_sinks.empty())
            {
                std::cout << "当前日志器：" << _logger_name << " 未检测到落地方向，默认设置为标准输出！\n";
                _sinks.push_back(std::make_shared<StdoutSink>());
            }
            Logger::ptr lp;
            if (_logger_type == Logger::Type::LOGGER_ASYNC)
            {
                lp = std::make_shared<AsyncLogger>(_logger_name, _formatter, _sinks, _level);
            }
            else
            {
                lp = std::make_shared<SyncLogger>(_logger_name, _formatter, _sinks, _level);
            }
            LoggerManager::GetInstance().AddLogger(_logger_name, lp);
            return lp;
        }
    };
};
